from marshmallow import fields, ValidationError


class FieldWithPreDeserialize(fields.Field):
    def __init__(self, *args, pre_deserialize=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.pre_deserialize = pre_deserialize

    def _deserialize(self, value, attr, data, **kwargs):
        try:
            if self.pre_deserialize:
                value = self.pre_deserialize(value)
            return value
        except Exception:
            raise ValidationError(f'failed to pre_deserialize {repr(value)} '
                                  f'with {self.pre_deserialize}')


class IntBool(FieldWithPreDeserialize):
    def __init__(self, *args, mapping=None, **kwargs):
        self.mapping = mapping or {'1': True, '0': False}
        super().__init__(*args, **kwargs)

    def _deserialize(self, value, attr, data, **kwargs):
        try:
            value = super()._deserialize(value, attr, data, **kwargs)
            return self.mapping[str(value)]
        except KeyError:
            raise ValidationError(f'{repr(value)} is not Bool')


class ModelField(FieldWithPreDeserialize):
    def __init__(self, model_cls, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.model_cls = model_cls

    def _deserialize(self, value, attr, data, **kwargs):
        try:
            value = super()._deserialize(value, attr, data, **kwargs)
            return self.model_cls(value)
        except (ValueError, TypeError):
            raise ValidationError(f'{repr(value)} is not {self.model_cls}')

    def _serialize(self, value, attr, obj, **kwargs):
        return value.value


class EnumField(ModelField):
    pass


class CsvField(fields.Field):
    def __init__(self, *args, cast_func=int, delimiter=',', **kwargs):
        super().__init__(*args, **kwargs)
        self.cast_func = cast_func
        self.delimiter = delimiter

    def _deserialize(self, value, attr, data, **kwargs):
        try:
            return [
                self.cast_func(item) for item in value.split(self.delimiter)
            ]
        except ValueError as err:
            raise ValidationError(f'failed to load csv data: {err}')
